#pragma once

#include <vector>
#include <semaphore.h>
#include <pthread.h>

template <class T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }

    void V(sem_t &sem)
    {
        sem_post(&sem);
    }

public:
    RingQueue(size_t cap = 5) : _cap(cap), _stepC(0), _stepP(0)
    {
        _ringQueue.resize(5);
        sem_init(&_spaceSem, 0, _cap);
        sem_init(&_dataSem, 0, 0);

        pthread_mutex_init(&_mutexC, nullptr);
        pthread_mutex_init(&_mutexP, nullptr);
    }

    // 消费者
    void Pop(T *out)
    {
        P(_dataSem);
        pthread_mutex_lock(&_mutexC);
        *out = _ringQueue[_stepC];
        ++_stepC;
        _stepC %= _cap;
        pthread_mutex_unlock(&_mutexC);
        V(_spaceSem);
    }

    // 生产者
    void Push(const T &in)
    {
        P(_spaceSem);
        pthread_mutex_lock(&_mutexP);
        _ringQueue[_stepP] = in;
        ++_stepP;
        _stepP %= _cap;
        pthread_mutex_unlock(&_mutexP);
        V(_dataSem);
    }

    ~RingQueue()
    {
        sem_destroy(&_spaceSem);
        sem_destroy(&_dataSem);

        pthread_mutex_destroy(&_mutexC);
        pthread_mutex_destroy(&_mutexP);
    }

private:
    std::vector<T> _ringQueue;
    size_t _cap;
    size_t _stepC;
    size_t _stepP;
    sem_t _spaceSem;
    sem_t _dataSem;
    pthread_mutex_t _mutexC;
    pthread_mutex_t _mutexP;
};